package com.foo.classifiers;

import java.util.Random;

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.functions.SMO;
import weka.classifiers.lazy.IBk;
import weka.core.Instances;
import weka.core.SelectedTag;
import weka.core.Tag;
import weka.core.Utils;

import com.foo.constants.Constants;
import com.foo.preprocessing.CreatingArffFile;
import com.foo.preprocessing.PreprocessedDataset;

/*
 * 
 */
public class KNN_Classifier_10NN_DistanceWeighting_options
{
	
	/*
	 * Build an KNN classifier by calling creating an ARFF file with different
	 * attribute combinations and applying StringToNominal filter for category attribute
	 * and StringToVector filter. After building classifier the classifier is evaluated 
	 * by CrossValidation evaluation technique where the noOfFolds and noOfSeeds are entered 
	 */
	public Evaluation buildIBkClassifier(Instances rawData, int noOfFolds, int noOfSeeds)
	{
		Instances filteredData;
		Evaluation evaluation = null;
		int numberOfNearestNeighours = 10;
		try
		{
			//Step 2: Apply Filters
			//Apply StringtoNominal filter for the category attribute
			PreprocessedDataset preprocessedObj = new PreprocessedDataset();
			filteredData = preprocessedObj.Apply_String_To_Nominal_Filters(rawData);
			
			//Apply StrintoWordVector filter for the remaining attribute
			filteredData = preprocessedObj.Apply_String_To_Vector_Filters(filteredData);			
			
			//Step 3: Build Classifier with 3 nearest neighbors
			IBk IBkClassifier = new IBk(numberOfNearestNeighours);
			
//			Setting different weights. Uncomment weight options to run kNN with weights
//			IBkClassifier.setDistanceWeighting(new SelectedTag(IBk.WEIGHT_NONE,IBk.TAGS_WEIGHTING));
			IBkClassifier.setDistanceWeighting(new SelectedTag(IBk.WEIGHT_INVERSE,IBk.TAGS_WEIGHTING));
//			IBkClassifier.setDistanceWeighting(new SelectedTag(IBk.WEIGHT_SIMILARITY,IBk.TAGS_WEIGHTING));

			
			IBkClassifier.buildClassifier(filteredData);
			
			//Step 4: Validate Classifier using cross validation technique
		    evaluation = new Evaluation(filteredData);
		    evaluation.crossValidateModel(IBkClassifier, filteredData, noOfFolds, new Random(noOfSeeds));
		}
		catch(Exception e)
		{
			System.err.println("Error Building classifier");
		}
		return evaluation;
	}
	
	/*
	 * Creating a KNN Classifier with all feature selection
	 */
	public Evaluation createInstance_All_Features(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile();
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}
	
	/*
	 * Creating a KNN Classifier with only Source feature selection
	 */
	public Evaluation createInstance_Source(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.SOURCE_ATTRIBUTE);
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Creating a KNN Classifier with only Title feature selection
	 */
	public Evaluation createInstance_Title(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.TITLE_ATTRIBUTE);
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Creating a KNN Classifier with only Description feature selection
	 */
	public Evaluation createInstance_Desc(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.DESCRIPTION_ATTRIBUTE);
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Creating a KNN Classifier with Source and Title feature selection
	 */
	public Evaluation createInstance_Source_Title(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.SOURCE_ATTRIBUTE,Constants.TITLE_ATTRIBUTE);
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Creating a KNN Classifier with Source and Description feature selection
	 */
	public Evaluation createInstance_Source_Description(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.SOURCE_ATTRIBUTE,Constants.DESCRIPTION_ATTRIBUTE);
			
			evaluation= buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Creating a KNN Classifier with Title and Description feature selection
	 */
	public Evaluation createInstance_Title_Description(int noOfFolds, int noOfSeeds)
	{
		Instances rawData =null;
		Evaluation evaluation = null;
		try
		{			
			//Step1: Creating Weka specific data instance
			CreatingArffFile createObj = new CreatingArffFile();
			rawData = createObj.create_ArffFile(Constants.TITLE_ATTRIBUTE,Constants.DESCRIPTION_ATTRIBUTE);
			
			evaluation = buildIBkClassifier(rawData, noOfFolds, noOfSeeds);
		}
		catch(Exception e)
		{
			System.err.println("Error Creating Arff file in KNN_Classifier_DistanceWeighting:" + e.getMessage());
		}
		return evaluation;
	}

	/*
	 * Main Function to test the SVM classifier with different feature selection 
	 * and different parameter setting
	 */
	public static void main(String[] args) throws Exception 
	{
		KNN_Classifier_10NN_DistanceWeighting_options KNN = new KNN_Classifier_10NN_DistanceWeighting_options();
		
		int noOfFolds = 10;
		int noOfSeeds = 1;
		
		Evaluation evaluation = null;
		StringBuilder output = null;
		
		//Case 1: Building the classifier with all instances and validating
		evaluation =  KNN.createInstance_All_Features(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append("\n*******************************************************************\n");
		output.append(
			"Case 1: Building the classifier with all instances Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 1"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 2: Building the classifier with only Source feature
		evaluation = KNN.createInstance_Source(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
			"Case 2: Building the classifier with only Source feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 2"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 3: Building the classifier with only Title feature
		evaluation = KNN.createInstance_Title(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
				"Case 3: Building the classifier with only Title feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect() +"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 3"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 4: Building the classifier with only Description feature
		evaluation = KNN.createInstance_Desc(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
				"Case 4: Building the classifier with only Description feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 4"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 5: Building the classifier with only Source and Title feature
		evaluation = KNN.createInstance_Source_Title(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
			"Case 5: Building the classifier with only Source and Title feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 5"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 6: Building the classifier with only Source and Description feature
		evaluation = KNN.createInstance_Source_Description(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
			"Case 6: Building the classifier with only Source and Description feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 6"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
		
		//Case 7: Building the classifier with only Title and Description feature
		evaluation = KNN.createInstance_Title_Description(noOfFolds, noOfSeeds);
		
		output = new StringBuilder();
		output.append(
				"Case 7: Building the classifier with only Title and Description feature Output:");
		output.append("\nTotal Instance: " + evaluation.numInstances());
		output.append("\nPercentage of Correctly Classified Instance: " + evaluation.pctCorrect()+"%");
		output.append("\nPercentage of InCorrectly Classified Instance: " + evaluation.pctIncorrect() +"%");
		output.append("\nPercentage of UnClassified Instances: " + evaluation.pctUnclassified() +"%");
		output.append("\nAccuracy of Classifier: " + (evaluation.correct() / evaluation.numInstances()));
		output.append("\nError Rate of Classifier: " + evaluation.errorRate());
		output.append("\nConfusion Matrix of the Classifier: \n" + evaluation.toMatrixString("Case 7"));
		output.append("\nWeighted Precision of Classifier: " + evaluation.weightedPrecision());
		output.append("\nWeighted Recall of Classifier: " + evaluation.weightedRecall());
		output.append("\n*******************************************************************\n");
		
		System.out.println(output.toString());
	}

}
